/**
 * @file HTTP 请求/响应头拦截与修改（主进程）
 * @description 在 Electron 主进程通过 session.webRequest 拦截 HTTP 请求与响应头，
 *              基于“静态规则配置”执行增/改/删，仅支持字符串包含 URL 匹配方式。
 *
 * 设计目标：
 * 1) 规则静态注册：从配置文件加载，进程启动后注册一次，不提供运行时增删。
 * 2) 性能优化：
 *    - URL 预过滤：尽可能从规则推导出 URL 过滤器（urls 数组），避免全量监听；
 *    - 二次快速匹配：监听器内部对匹配规则快速判断，未命中则直接透传；
 * 3) 健壮性：大小写无关的头部合并；完整异常处理；中文日志；
 * 4) 可扩展：请求头、响应头分别配置与处理。
 */

import {
  type HeadersReceivedResponse,
  type OnBeforeSendHeadersListenerDetails,
  type OnHeadersReceivedListenerDetails,
  session
} from 'electron'

// =========================
// 类型定义
// =========================

/** URL 匹配器：字符串包含匹配 */
export type UrlPattern = string
/** 允许多个 URL 片段使用同一套规则 */
export type MatchPattern = string | string[]

/** 头部修改：值为 string/string[] 表示设置/新增；为 null 表示删除该字段 */
export type HeaderChangeMap = Record<string, string | string[] | null>

export interface RequestHeaderRule {
  match: MatchPattern
  handler: (details: OnBeforeSendHeadersListenerDetails) => HeaderChangeMap | null | undefined
}

export interface ResponseHeaderRule {
  match: MatchPattern
  handler: (details: OnHeadersReceivedListenerDetails) => HeaderChangeMap | null | undefined
}

export interface HeaderRulesConfig {
  requests?: RequestHeaderRule[]
  responses?: ResponseHeaderRule[]
}

// =========================
// 内部状态
// =========================

let requestListenerRegistered = false
let responseListenerRegistered = false

// =========================
// 工具函数：头部大小写无关的标准化/反标准化
// =========================

function normalizeHeaders(input: Record<string, string | string[] | undefined> | undefined) {
  const map = new Map<string, string[]>()
  if (!input) return map
  for (const [k, v] of Object.entries(input)) {
    if (v == null) continue
    const lower = k.toLowerCase()
    const values = Array.isArray(v) ? v.map(String) : [String(v)]
    map.set(lower, values)
  }
  return map
}

// 兼容保留：若后续需要恢复使用可参考 commit 历史
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function denormalizeHeaders(_norm: Map<string, string[]>) {
  const out: Record<string, string | string[]> = {}
  return out
}

function applyHeaderChanges(base: Map<string, string[]>, changes: HeaderChangeMap) {
  for (const [key, value] of Object.entries(changes)) {
    const k = key.toLowerCase()
    if (value === null) {
      base.delete(k)
    } else if (Array.isArray(value)) {
      base.set(k, value.map(String))
    } else {
      base.set(k, [String(value)])
    }
  }
}

// =========================
// 工具函数：URL 匹配与过滤器推导
// =========================

function makeMatcher(pattern: MatchPattern): (url: string) => boolean {
  const needles = Array.isArray(pattern) ? pattern.map(String) : [String(pattern)]
  return (url: string) => needles.some(n => url.includes(n))
}

/**
 * 根据规则集合推导 webRequest 的 { urls } 过滤器，尽量收敛匹配范围。
 * - 对字符串匹配：若像域名（含点且不含空白），生成 host 与 *.host 的 http/https 变体
 * - 其他字符串：无法推导域名时回退 all-schemes（例如 *://any.host/*）
 */
export function buildUrlFilters(rules: Array<RequestHeaderRule | ResponseHeaderRule>): string[] {
  const filters = new Set<string>()
  const pushFromString = (sIn: string) => {
    const s = sIn.trim()
    if (s.startsWith('http://') || s.startsWith('https://')) {
      try {
        const u = new URL(s)
        addHost(u.host as string, u.protocol === 'http:' ? 'http' : 'https')
      } catch {
        /* noop */
      }
    } else if (s.includes('.') && !s.includes(' ')) {
      addHost(s, 'both')
    } else {
      filters.add('*://*/*')
    }
  }
  const addHost = (host: string, scheme?: 'http' | 'https' | 'both') => {
    const exact = host.startsWith('*.') ? host.slice(2) : host
    const wildcard = host.startsWith('*.') ? host : `*.${host}`
    const addFor = (sch: 'http' | 'https') => {
      filters.add(`${sch}://${exact}/*`)
      filters.add(`${sch}://${wildcard}/*`)
    }
    if (!scheme || scheme === 'both') {
      addFor('http')
      addFor('https')
    } else {
      addFor(scheme)
    }
  }

  for (const r of rules) {
    const arr = Array.isArray(r.match) ? r.match : [r.match]
    for (const s of arr) pushFromString(s)
  }

  // 最多返回几百条对性能影响也很小；为空则用全站
  if (filters.size === 0) filters.add('*://*/*')
  return Array.from(filters)
}

// =========================
// 对外：注册入口
// =========================

/**
 * 注册 HTTP 请求/响应头拦截器（仅注册一次）
 * - 从传入的静态规则配置中构建 URL 过滤器并绑定 webRequest 监听
 */
export function registerHttpHeaderInterceptors(
  config: HeaderRulesConfig,
  targetSession = session.defaultSession
) {
  const requestRules = config.requests ?? []
  const responseRules = config.responses ?? []

  // 构建匹配器，避免监听器内重复创建
  const compiledRequest = requestRules.map(r => ({
    matcher: makeMatcher(r.match),
    handler: r.handler
  }))
  const compiledResponse = responseRules.map(r => ({
    matcher: makeMatcher(r.match),
    handler: r.handler
  }))

  // 注册请求头
  if (!requestListenerRegistered && compiledRequest.length > 0) {
    const urls = buildUrlFilters(requestRules)
    try {
      targetSession.webRequest.onBeforeSendHeaders({ urls }, (details, callback) => {
        try {
          // 二次预过滤
          let hit = false
          for (const r of compiledRequest) {
            if (r.matcher(details.url)) {
              hit = true
              break
            }
          }
          if (!hit) return callback({ requestHeaders: details.requestHeaders })

          const base = normalizeHeaders(details.requestHeaders as Record<string, string | string[]>)
          for (const r of compiledRequest) {
            try {
              if (r.matcher(details.url)) {
                const changes = r.handler(details)
                if (changes) applyHeaderChanges(base, changes)
              }
            } catch (error) {
              console.error('请求头规则执行异常：', error)
            }
          }
          callback({
            requestHeaders: Object.fromEntries(
              [...base.entries()].map(([k, v]) => [k, v.length <= 1 ? (v[0] ?? '') : v])
            )
          })
        } catch (error) {
          console.error('处理 onBeforeSendHeaders 发生异常：', error)
          callback({ requestHeaders: details.requestHeaders })
        }
      })
      requestListenerRegistered = true
      console.log(`[网络] 已注册请求头拦截器，过滤 ${urls.length} 条 URL 模式`)
    } catch (error) {
      console.error('注册请求头拦截器失败：', error)
    }
  }

  // 注册响应头
  if (!responseListenerRegistered && compiledResponse.length > 0) {
    const urls = buildUrlFilters(responseRules)
    try {
      targetSession.webRequest.onHeadersReceived({ urls }, (details, callback) => {
        try {
          // 二次预过滤
          let hit = false
          for (const r of compiledResponse) {
            if (r.matcher(details.url)) {
              hit = true
              break
            }
          }
          if (!hit) return callback({ responseHeaders: details.responseHeaders })

          const base = normalizeHeaders(
            details.responseHeaders as Record<string, string | string[]>
          )
          for (const r of compiledResponse) {
            try {
              if (r.matcher(details.url)) {
                const changes = r.handler(details)
                if (changes) applyHeaderChanges(base, changes)
              }
            } catch (error) {
              console.error('响应头规则执行异常：', error)
            }
          }
          // 返回全部小写键名 + 数组值，确保 Chromium 正确识别
          const out: HeadersReceivedResponse = {
            responseHeaders: Object.fromEntries([...base.entries()].map(([k, v]) => [k, v]))
          }
          callback(out)
        } catch (error) {
          console.error('处理 onHeadersReceived 发生异常：', error)
          callback({ responseHeaders: details.responseHeaders })
        }
      })
      responseListenerRegistered = true
      console.log(`[网络] 已注册响应头拦截器，过滤 ${urls.length} 条 URL 模式`)
    } catch (error) {
      console.error('注册响应头拦截器失败：', error)
    }
  }
}

// =========================
// 便捷导出：示例规则配置入口
// =========================

export { default as defaultHeaderRules } from './header-rules.config'
